package com.whcoding.test.common.list;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @program: spring-boot-learning
 * @description: 去重根据指定参数
 * @author: whcoding
 * @create: 2022-09-27 14:50
 **/
public class DistinctByPropertyUtils {

	/**
	 * 根据指定字段去重
	 * @param keyExtractor
	 * @param <T>
	 * @return
	 */
	public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
		Map<Object,Boolean> seen = new ConcurrentHashMap<>();
		return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
	}
	/**
	 * 对于某实体类，指定要去重集合和去重属性字段，进行去重
	 *
	 * @param tList     要去集合集合
	 * @param propertys 属性列表
	 * @param <T>       申明泛型
	 * @return List<T> 去重后集合
	 */
	public static <T> List<T> distinctByProperty(T t, List<T> tList, String... propertys) {

		List<T> resList = new ArrayList<>();
		Map<String, Method> readMethodMap = new HashMap<>(16);
		Map<String, Method> writeMethodMap = new HashMap<>(16);
		List<Field> fieldList = Arrays.asList(t.getClass().getDeclaredFields());
		for (Field field : fieldList) {
			try {
				PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), t.getClass());
				Method readMethod = descriptor.getReadMethod();
				Method writeMethod = descriptor.getWriteMethod();
				readMethodMap.put(field.getName(), readMethod);
				writeMethodMap.put(field.getName(), writeMethod);
			} catch (IntrospectionException e) {
				e.printStackTrace();
			}
		}
		List<String> propertyList = Arrays.asList(propertys);
		try {
			for (T t1 : tList) {
				if (resList.size() == 0) {
					resList.add(t1);
					continue;
				}
				Integer sumIsAddNum = 0;
				for (T t2 : resList) {
					boolean isAdd = false;
					for (String property : propertyList) {
						Object value1 = readMethodMap.get(property).invoke(t2);
						Object value2 = readMethodMap.get(property).invoke(t1);
						if (!value1.equals(value2)) {
							isAdd = true;
						}
					}
					if (isAdd) {
						sumIsAddNum = sumIsAddNum + 1;
					}
				}
				if (sumIsAddNum.equals(resList.size())) {
					resList.add(t1);
				}
			}
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return resList;
	}


	/**
	 * 对于某实体类，指定集合，通过指定字段和字段值进行筛选
	 *
	 * @param tList            要通过字段筛选的集合
	 * @param propertyValueMap 属性和值映射列表 key 为字段名称 value 为字段的值
	 * @param <T>              申明泛型
	 * @return List<T> 通过指定字段和字段值进行筛选后的集合
	 */
	public static <T> List<T> filterByProperty(T t, List<T> tList, Map<String, Object> propertyValueMap) {

		List<T> resList = new ArrayList<>();
		Map<String, Method> readMethodMap = new HashMap<>(16);
		Map<String, Method> writeMethodMap = new HashMap<>(16);
		List<Field> fieldList = Arrays.asList(t.getClass().getDeclaredFields());
		for (Field field : fieldList) {
			try {

				if ("serialVersionUID".equals(field.getName())){
					System.out.println("获取字段以及字段类型为:"+field.getName()+field.getType());
					continue;
				}
				PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), t.getClass());
				Method readMethod = descriptor.getReadMethod();
				Method writeMethod = descriptor.getWriteMethod();
				readMethodMap.put(field.getName(), readMethod);
				writeMethodMap.put(field.getName(), writeMethod);
			} catch (IntrospectionException e) {
				e.printStackTrace();
			}
		}
		Set<String> propertySet = propertyValueMap.keySet();
		try {
			for (T t1 : tList) {
				boolean isAdd = true;
				for (String property : propertySet) {
					Object value1 = propertyValueMap.get(property);
					Object value2 = readMethodMap.get(property).invoke(t1);
					if (!value1.equals(value2)) {
						isAdd = false;
					}
				}
				if (isAdd) {
					resList.add(t1);
				}
			}
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return resList;
	}


}
